// Cpw_02 [Cpw-OpenGL Teapot].nova



// Using namespace declarations.
using Library.CPW;
using Library.OpenGL;



// The application class.
class CpwTeapotApp
{
   // Static data members.
   private static uint teaList;
   private static float rotX, rotY;
   private static float rotationSpeed;
   private static int drawFrame;
   private static CPW cpw;


   // Application class's "main" function.
   public static void main( String[] args )
   {
      // Initialize the static data members.
      rotX = rotY = 0.0f;
      rotationSpeed = 4.0f;
      drawFrame = 0;


      // Output the keyboard contols.
      Stream.write( "Keyboard controls:\r\n" +
                    "\tleft  = rotate y-axis -ve\r\n" +
                    "\tright = rotate y-axis +ve\r\n" +
                    "\tup    = rotate x-axis -ve\r\n" +
                    "\tdown  = rotate x-axis +ve\r\n" +
                    "\tesc   = exit\r\n" );


      // Create a new CPW context object.
      cpw = new CPW( );

      // Initialize the CPW context object.
      cpw.cpwInitContext( );

      // Create a window.
      cpw.cpwCreateWindowEx( "Cpw_02 [Cpw-OpenGL Teapot]",
                             100,
                             100,
                             640,
                             640 );

      // Initialize OpenGL.
      init( );

      // Register the event callbacks.
      cpw.cpwCreateCallback( createCallback );
      cpw.cpwDisplayCallback( displayCallback );
      cpw.cpwIdleCallback( idleCallback );
      cpw.cpwReshapeCallback( reshapeCallback );
      cpw.cpwSystemKeyboardCallback( systemKeyboardCallback );

      // Enter the main rendering loop.
      cpw.cpwMainLoop( );

      // Shutdown the CPW context.
      cpw.cpwFreeContext( );
   }


   // Initialize OpenGL.
   private static void init( )
   {
      float[] position   = { 0.0f, 3.0f, 3.0f, 0.0f };
      float[] localView  = { 0.0f };
      float[] ambient    = { 0.1745f, 0.01175f, 0.01175f };
      float[] diffuse    = { 0.61424f, 0.04136f, 0.04136f };
      float[] specular   = { 0.727811f, 0.626959f, 0.626959f };

      OpenGL.glEnable( OpenGL.GL_DEPTH_TEST );
      OpenGL.glDepthFunc( OpenGL.GL_LESS );

      OpenGL.glLightfv( OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, position );
      OpenGL.glLightModelfv( OpenGL.GL_LIGHT_MODEL_LOCAL_VIEWER, localView );

      OpenGL.glFrontFace( OpenGL.GL_CW );
      OpenGL.glEnable( OpenGL.GL_LIGHTING );
      OpenGL.glEnable( OpenGL.GL_LIGHT0 );
      OpenGL.glEnable( OpenGL.GL_AUTO_NORMAL );
      OpenGL.glEnable( OpenGL.GL_NORMALIZE );

      OpenGL.glMaterialfv( OpenGL.GL_FRONT, OpenGL.GL_AMBIENT, ambient );
      OpenGL.glMaterialfv( OpenGL.GL_FRONT, OpenGL.GL_DIFFUSE, diffuse );
      OpenGL.glMaterialfv( OpenGL.GL_FRONT, OpenGL.GL_SPECULAR, specular );
      OpenGL.glMaterialf( OpenGL.GL_FRONT, OpenGL.GL_SHININESS, 0.6f * 128.0f );

      OpenGL.glClearColor( 0.5f, 0.5f, 0.5f, 0.0f );
      OpenGL.glColor3f( 1.0f, 1.0f, 1.0f );
      teapot( 14 );
   }


   // Window create / close event callback.
   private static void createCallback( CPW cpw, uint winID, bool flag )
   {
      if ( flag == false )
      {
         // Window close event.
         cpw.cpwDestroyWindow( winID );
      }
   }


   // Window draw event callback.
   private static void displayCallback( CPW cpw, uint winID )
   {
      // Check if there are any frames to render.
      // Double buffering uses two frames.
      if ( drawFrame < 2 )
      {
         // Render the teapot.
         OpenGL.glClear( OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT );

         OpenGL.glPushMatrix( );

         OpenGL.glTranslatef( 0.0f, 0.0f, -5.0f );
         OpenGL.glRotatef( rotY, 0.0f, 1.0f, 0.0f );
         OpenGL.glRotatef( rotX, 1.0f, 0.0f, 0.0f );

         OpenGL.glCallList( teaList );

         OpenGL.glPopMatrix( );

         drawFrame++;
      }

      cpw.cpwSwapWindowBuffers( winID );
   }


   // Idle event callback.
   private static void idleCallback( CPW cpw )
   {
      if ( cpw.cpwKeyState( CPW.CPW_KEY_LEFT ) )
      {
         rotY -= rotationSpeed;
         drawFrame = 0;
      }

      if ( cpw.cpwKeyState( CPW.CPW_KEY_RIGHT ) )
      {
         rotY += rotationSpeed;
         drawFrame = 0;
      }

      if ( cpw.cpwKeyState( CPW.CPW_KEY_UP ) )
      {
         rotX -= rotationSpeed;
         drawFrame = 0;
      }

      if ( cpw.cpwKeyState( CPW.CPW_KEY_DOWN ) )
      {
         rotX += rotationSpeed;
         drawFrame = 0;
      }

      cpw.cpwPostRedisplay( );
   }


   // Window resize event callback.
   private static void reshapeCallback( CPW cpw, uint winID, uint w, uint h )
   {
      OpenGL.glViewport( 0, 0, (int)w, (int)h );

      OpenGL.glMatrixMode( OpenGL.GL_PROJECTION );
      OpenGL.glLoadIdentity( );
      OpenGL.glOrtho( -6.0, 6.0, -6.0, 6.0, -1.0, 10.0 );
      OpenGL.glMatrixMode( OpenGL.GL_MODELVIEW );
      OpenGL.glLoadIdentity( );

      drawFrame = 0;
   }


   // System keyboard event callback.
   private static void systemKeyboardCallback( CPW cpw,
                                               uint id,
                                               uint key,
                                               uint state,
                                               uint x,
                                               uint y )
   {
      if ( key == CPW.CPW_KEY_ESCAPE )
      {
         cpw.cpwBreakMainLoop( );
      }
   }


   // Create the teapot's geometry.
   private static void teapot( int grid )
   {
      int[][] patchData =
      {
         { 102, 103, 104, 105,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15 },
         {  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27 },
         {  24,  25,  26,  27,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40 },
         {  96,  96,  96,  96,  97,  98,  99, 100, 101, 101, 101, 101,   0,   1,   2,   3 },
         {   0,   1,   2,   3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117 },
         { 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120,  40,  39,  38,  37 },
         {  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56 },
         {  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,  28,  65,  66,  67 },
         {  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83 },
         {  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95 }
      };

      double[][] cpData =
      {
         { 0.2, 0.0, 2.7 }, { 0.2, -0.112, 2.7 }, { 0.112, -0.2, 2.7 }, { 0.0, -0.2, 2.7 },
         { 1.3375, 0.0, 2.53125 }, { 1.3375, -0.749, 2.53125 }, { 0.749, -1.3375, 2.53125 },
         { 0.0, -1.3375, 2.53125 }, { 1.4375, 0.0, 2.53125 }, { 1.4375, -0.805, 2.53125 },
         { 0.805, -1.4375, 2.53125 }, { 0.0, -1.4375, 2.53125 }, { 1.5, 0.0, 2.4 }, { 1.5, -0.84, 2.4 },
         { 0.84, -1.5, 2.4 }, { 0.0, -1.5, 2.4 }, { 1.75, 0.0, 1.875 }, { 1.75, -0.98, 1.875 },
         { 0.98, -1.75, 1.875 }, { 0.0, -1.75, 1.875 }, { 2.0, 0.0, 1.35 }, { 2.0, -1.12, 1.35 },
         { 1.12, -2.0, 1.35 }, { 0.0, -2.0, 1.35 }, { 2.0, 0.0, 0.9 }, { 2.0, -1.12, 0.9 }, { 1.12, -2.0, 0.9 },
         { 0.0, -2.0, 0.9 }, { -2.0, 0.0, 0.9 }, { 2.0, 0.0, 0.45 }, { 2.0, -1.12, 0.45 }, { 1.12, -2.0, 0.45 },
         { 0.0, -2.0, 0.45 }, { 1.5, 0.0, 0.225 }, { 1.5, -0.84, 0.225 }, { 0.84, -1.5, 0.225 },
         { 0.0, -1.5, 0.225 }, { 1.5, 0.0, 0.15 }, { 1.5, -0.84, 0.15 }, { 0.84, -1.5, 0.15 },
         { 0.0, -1.5, 0.15 }, { -1.6, 0.0, 2.025 }, { -1.6, -0.3, 2.025 }, { -1.5, -0.3, 2.25 },
         { -1.5, 0.0, 2.25 }, { -2.3, 0.0, 2.025 }, { -2.3, -0.3, 2.025 }, { -2.5, -0.3, 2.25 },
         { -2.5, 0.0, 2.25 }, { -2.7, 0.0, 2.025 }, { -2.7, -0.3, 2.025 }, { -3.0, -0.3, 2.25 },
         { -3.0, 0.0, 2.25 }, { -2.7, 0.0, 1.8 }, { -2.7, -0.3, 1.8 }, { -3.0, -0.3, 1.8 }, { -3.0, 0.0, 1.8 },
         { -2.7, 0.0, 1.575 }, { -2.7, -0.3, 1.575 }, { -3.0, -0.3, 1.35 }, { -3.0, 0.0, 1.35 },
         { -2.5, 0.0, 1.125 }, { -2.5, -0.3, 1.125 }, { -2.65, -0.3, 0.9375 }, { -2.65, 0.0, 0.9375 },
         { -2.0, -0.3, 0.9 }, { -1.9, -0.3, 0.6 }, { -1.9, 0.0, 0.6 }, { 1.7, 0.0, 1.425 },
         { 1.7, -0.66, 1.425 }, { 1.7, -0.66, 0.6 }, { 1.7, 0.0, 0.6 }, { 2.6, 0.0, 1.425 },
         { 2.6, -0.66, 1.425 }, { 3.1, -0.66, 0.825 }, { 3.1, 0.0, 0.825 }, { 2.3, 0.0, 2.1 },
         { 2.3, -0.25, 2.1 }, { 2.4, -0.25, 2.025 }, { 2.4, 0.0, 2.025 }, { 2.7, 0.0, 2.4 },
         { 2.7, -0.25, 2.4 }, { 3.3, -0.25, 2.4 }, { 3.3, 0.0, 2.4 }, { 2.8, 0.0, 2.475 },
         { 2.8, -0.25, 2.475 }, { 3.525, -0.25, 2.49375 }, { 3.525, 0.0, 2.49375 },
         { 2.9, 0.0, 2.475 }, { 2.9, -0.15, 2.475 }, { 3.45, -0.15, 2.5125 }, { 3.45, 0.0, 2.5125 },
         { 2.8, 0.0, 2.4 }, { 2.8, -0.15, 2.4 }, { 3.2, -0.15, 2.4 }, { 3.2, 0.0, 2.4 }, { 0.0, 0.0, 3.15 },
         { 0.8, 0.0, 3.15 }, { 0.8, -0.45, 3.15 }, { 0.45, -0.8, 3.15 }, { 0.0, -0.8, 3.15 },
         { 0.0, 0.0, 2.85 }, { 1.4, 0.0, 2.4 }, { 1.4, -0.784, 2.4 }, { 0.784, -1.4, 2.4 }, { 0.0, -1.4, 2.4 },
         { 0.4, 0.0, 2.55 }, { 0.4, -0.224, 2.55 }, { 0.224, -0.4, 2.55 }, { 0.0, -0.4, 2.55 },
         { 1.3, 0.0, 2.55 }, { 1.3, -0.728, 2.55 }, { 0.728, -1.3, 2.55 }, { 0.0, -1.3, 2.55 },
         { 1.3, 0.0, 2.4 }, { 1.3, -0.728, 2.4 }, { 0.728, -1.3, 2.4 }, { 0.0, -1.3, 2.4 }, { 0.0, 0.0, 0.0 },
         { 1.425, -0.798, 0.0 }, { 1.5, 0.0, 0.075 }, { 1.425, 0.0, 0.0 }, { 0.798, -1.425, 0.0 },
         { 0.0, -1.5, 0.075 }, { 0.0, -1.425, 0.0 }, { 1.5, -0.84, 0.075 }, { 0.84, -1.5, 0.075 }
      };

      double[][][] p = createArray( 4, 4, 3 ),
                   q = createArray( 4, 4, 3 ),
                   r = createArray( 4, 4, 3 ),
                   s = createArray( 4, 4, 3 );

      int i, j, k, l;

      teaList = OpenGL.glGenLists( 1 );
      OpenGL.glNewList( teaList, OpenGL.GL_COMPILE );
      OpenGL.glPushMatrix( );
      OpenGL.glRotatef( 270.0f, 1.0f, 0.0f, 0.0f );

      for ( i = 0; i < 10; i++ )
      {
         for ( j = 0; j < 4; j++ )
         {
            for ( k = 0; k < 4; k++ )
            {
               for ( l = 0; l < 3; l++ )
               {
                  p[ j ][ k ][ l ] = cpData[ patchData[ i ][ j * 4 + k ] ][ l ];
                  q[ j ][ k ][ l ] = cpData[ patchData[ i ][ j * 4 + ( 3 - k ) ] ][ l ];

                  if ( l == 1 )
                  {
                     q[ j ][ k ][ l ] *= -1.0;
                  }

                  if ( i < 6 )
                  {
                     r[ j ][ k ][ l ] = cpData[ patchData[ i ][ j * 4 + ( 3 - k ) ] ][ l ];

                     if ( l == 0 )
                     {
                        r[ j ][ k ][ l ] *= -1.0;
                     }

                     s[ j ][ k ][ l ] = cpData[ patchData[ i ][ j * 4 + k ] ][ l ];

                     if ( l == 0 )
                     {
                        s[ j ][ k ][ l ] *= -1.0;
                     }

                     if ( l == 1 )
                     {
                        s[ j ][ k ][ l ] *= -1.0;
                     }
                  }
               }
            }
         }

         OpenGL.glMap2d( OpenGL.GL_MAP2_VERTEX_3, 0.0, 1.0, 3, 4, 0.0, 1.0, 12, 4, p );
         OpenGL.glEnable( OpenGL.GL_MAP2_VERTEX_3 );
         OpenGL.glMapGrid2d( grid, 0.0, 1.0, grid, 0.0, 1.0 );
         OpenGL.glEvalMesh2( OpenGL.GL_FILL, 0, grid, 0, grid );
         OpenGL.glMap2d( OpenGL.GL_MAP2_VERTEX_3, 0.0, 1.0, 3, 4, 0.0, 1.0, 12, 4, q );
         OpenGL.glEvalMesh2( OpenGL.GL_FILL, 0, grid, 0, grid );

         if ( i < 6 )
         {
            OpenGL.glMap2d( OpenGL.GL_MAP2_VERTEX_3, 0.0, 1.0, 3, 4, 0.0, 1.0, 12, 4, r );
            OpenGL.glEvalMesh2( OpenGL.GL_FILL, 0, grid, 0, grid );
            OpenGL.glMap2d( OpenGL.GL_MAP2_VERTEX_3, 0.0, 1.0, 3, 4, 0.0, 1.0, 12, 4, s );
            OpenGL.glEvalMesh2( OpenGL.GL_FILL, 0, grid, 0, grid );
         }
      }

      OpenGL.glDisable( OpenGL.GL_MAP2_VERTEX_3 );
      OpenGL.glPopMatrix( );
      OpenGL.glEndList( );
   }


   // Create a three dimensional array of doubles with the specified sizes.
   private static double[][][] createArray( uint size0, uint size1, uint size2 )
   {
      // Declare a local reference to the array to create.
      double[][][] array;

      // Create the first level of the array.
      array = new double[ size0 ][][];

      // Create the second levels of the array.
      for ( uint i = 0; i < size0; i++ )
      {
         array[ i ] = new double[ size1 ][];

         // Create the third levels of the array.
         for ( uint j = 0; j < size1; j++ )
         {
            array[ i ][ j ] = new double[ size2 ];

            // Populate the third level of the array.
            for ( uint k = 0; k < size2; k++ )
            {
               array[ i ][ j ][ k ] = 0.0;
            }
         }
      }

      // Return the new array.
      return array;
   }
}